package question.solution959;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class Solution959 {

	public int regionsBySlashes(String[] grid) {
		char[][] cleanGrid = getCharGrid(grid);
		LinkSet linkSet = initLinkSet(cleanGrid);
		linkSet.combineAllSet();

		return linkSet.getMapSize();
	}

	private char[][] getCharGrid(String[] grid){
		char[][] cleanGrid = new char[grid.length][];
		for(int i=0;i<grid.length; i++){
			cleanGrid[i] = grid[i].toCharArray();
		}
		return cleanGrid;
	}

	private LinkSet initLinkSet(char[][] grid) {
		SmallestArea[][][] smallAreaArray = new SmallestArea[grid.length][grid[0].length][4];
		LinkSet linkSet = new LinkSet();
		for (int i = 0,k=0; i < grid.length; i++) {
			for (int j = 0; j < grid[i].length; j++) {
				SmallestArea area0 = new SmallestArea();
				SmallestArea area1 = new SmallestArea();
				SmallestArea area2 = new SmallestArea();
				SmallestArea area3 = new SmallestArea();
				smallAreaArray[i][j][0] = area0;
				smallAreaArray[i][j][1] = area1;
				smallAreaArray[i][j][2] = area2;
				smallAreaArray[i][j][3] = area3;

				switch (grid[i][j]) {
				case '\\':
					Set set1 = new HashSet(2);
					k+=1;
					area0.setBelongSetIndex(k);
					area1.setBelongSetIndex(k);
					set1.add(area0);
					set1.add(area1);
					linkSet.addMap(k, set1);

					Set set2 = new HashSet(2);
					k+=1;
					area2.setBelongSetIndex(k);
					area3.setBelongSetIndex(k);
					set2.add(area2);
					set2.add(area3);
					linkSet.addMap(k, set2);

					break;
				case '/':
					Set set3 = new HashSet(2);
					k+=1;
					area0.setBelongSetIndex(k);
					area3.setBelongSetIndex(k);
					set3.add(area0);
					set3.add(area3);
					linkSet.addMap(k, set3);

					Set set4 = new HashSet(2);
					k+=1;
					area1.setBelongSetIndex(k);
					area2.setBelongSetIndex(k);
					set4.add(area1);
					set4.add(area2);
					linkSet.addMap(k, set4);
					break;
				case ' ':
					Set set5 = new HashSet(4);
					k+=1;
					area0.setBelongSetIndex(k);
					area1.setBelongSetIndex(k);
					area2.setBelongSetIndex(k);
					area3.setBelongSetIndex(k);
					set5.add(area0);
					set5.add(area1);
					set5.add(area2);
					set5.add(area3);
					linkSet.addMap(k, set5);
					break;
				default:
					break;
				}
			}
		}
		linkSet.setAreaIndexArray(smallAreaArray);
		return linkSet;
	}

	class LinkSet {

		private Map<Integer, Set<SmallestArea>> areaSetMap;

		private SmallestArea[][][] smallAreaIndexArray;

		public LinkSet() {
			areaSetMap = new HashMap<>();
		}

		public Set getSpecificSet(Integer setIndex) {
			return areaSetMap.get(setIndex);
		}

		public void combineTwoSet(Integer i1, Integer i2) {
			if(i1.equals(i2)){
				return;
			}
			Set firstSet = areaSetMap.get(i1);
			Set<SmallestArea> secondSet = areaSetMap.get(i2);
			for(SmallestArea element:secondSet){
				element.setBelongSetIndex(i1);
			}
			firstSet.addAll(secondSet);
			areaSetMap.remove(i2);
		}

		public void addMap(Integer index, Set set) {
			areaSetMap.put(index, set);
		}

		public void setAreaIndexArray(SmallestArea[][][] array){
			this.smallAreaIndexArray = array;
		}

		public void combineAllSet(){
			for(int i=0;i<smallAreaIndexArray.length;i++){
				for(int j=0;j<smallAreaIndexArray[i].length;j++){
					if(i<smallAreaIndexArray.length-1){
						SmallestArea downArea = smallAreaIndexArray[i][j][2];
						SmallestArea downCompanyArea = smallAreaIndexArray[i+1][j][0];
						combineTwoSet(downArea.getBelongSetIndex(), downCompanyArea.getBelongSetIndex());
					}
					if(j<smallAreaIndexArray.length-1){
						SmallestArea rightArea = smallAreaIndexArray[i][j][1];
						SmallestArea rightCompanyArea = smallAreaIndexArray[i][j+1][3];
						combineTwoSet(rightArea.getBelongSetIndex(), rightCompanyArea.getBelongSetIndex());
					}
				}
			}
		}

		public int getMapSize(){
			return this.areaSetMap.size();
		}
	}

	class SmallestArea{
		Integer belongSetIndex;

		public SmallestArea(){}

		public SmallestArea(int belongSetIndex){
			setBelongSetIndex(belongSetIndex);
		}

		public int getBelongSetIndex() {
			return belongSetIndex;
		}

		public void setBelongSetIndex(int belongSetIndex) {
			this.belongSetIndex = belongSetIndex;
		}
	}

	public boolean test(){
		String[] param = {"/\\/","/\\\\","\\ /"};

		int result = regionsBySlashes(param);
		if(5==result){
			return true;
		}
		return false;
	}
}
